package ly.count.ohos.sdk;

class ModuleDeviceId extends ModuleBase {
    boolean isExitTempIdAfterInit = false;

    ModuleLog moduleLog;

    ModuleDeviceId(Countly cly, CountlyConfig config) {
        super(cly);

        moduleLog = cly.L;
        moduleLog.v("[ModuleDeviceId] Initialising");

        boolean isCustomIdWasProvided = (config.deviceID != null);
        if (config.temporaryDeviceIdEnabled && !isCustomIdWasProvided) {
            // if we want to use temporary ID mode and no developer custom ID is provided
            // then we override that custom ID to set the temporary mode
            config.deviceID = DeviceId.temporaryCountlyDeviceId;
        }

        // choose what kind of device ID will be used
        if (config.deviceID != null) {
            // if the developer provided a ID
            // or it's a temporary ID
            config.deviceIdInstance = new DeviceId(config.countlyStore, config.deviceID, mCly.L);
        } else {
            // the dev provided only a type and the SDK should generate a appropriate ID
            config.deviceIdInstance = new DeviceId(config.countlyStore, config.idMode, mCly.L);
        }

        // initialise the set device ID value
        config.deviceIdInstance.init(config.context, config.countlyStore, true);

        boolean isTemporaryDeviceIdIsCurrentlyEnabled = config.deviceIdInstance.temporaryIdModeEnabled();
        moduleLog.d("[ModuleDeviceId] [TemporaryDeviceId] Temp ID should be enabled["
                + config.temporaryDeviceIdEnabled + "]"
                + " Currently enabled: [" + isTemporaryDeviceIdIsCurrentlyEnabled + "]");

        if (isTemporaryDeviceIdIsCurrentlyEnabled && isCustomIdWasProvided) {
            // if a custom ID was provided and we are still in temporary ID mode
            // it means the we had tempID mode at the previous app end
            // exit tempID after init finished
            moduleLog.d("[ModuleDeviceId] [TemporaryDeviceId]"
                    + " Decided we have to exit temporary device ID mode, mode enabled: ["
                    + config.temporaryDeviceIdEnabled + "], custom Device ID Set: [" + isCustomIdWasProvided + "]");

            isExitTempIdAfterInit = true;
        }
    }

    void exitTemporaryIdMode(DeviceId.Type type, String deviceId) {
        moduleLog.d("[ModuleDeviceId] Calling exitTemporaryIdMode");

        if (!mCly.isInitialized()) {
            throw new IllegalStateException("init must be called before exitTemporaryIdMode");
        }

        // start by changing stored ID
        mCly.connectionQueue_.getDeviceId()
                .changeToId(mCly.context_, mCly.connectionQueue_.getCountlyStore(), type, deviceId);

        // update stored request for ID change to use this new ID
        String[] storedRequests = mCly.connectionQueue_.getCountlyStore().connections();
        String temporaryIdTag = "&device_id=" + DeviceId.temporaryCountlyDeviceId;
        String newIdTag = "&device_id=" + deviceId;

        boolean isFoundOne = false;
        for (int a = 0; a < storedRequests.length; a++) {
            if (storedRequests[a].contains(temporaryIdTag)) {
                moduleLog.d("[ModuleDeviceId] [exitTemporaryIdMode] Found a tag to replace in: ["
                        + storedRequests[a] + "]");
                storedRequests[a] = storedRequests[a].replace(temporaryIdTag, newIdTag);
                isFoundOne = true;
            }
        }

        if (isFoundOne) {
            mCly.connectionQueue_.getCountlyStore().replaceConnections(storedRequests);
        }

        // update remote config_ values if automatic update is enabled
        mCly.remoteConfigClearValues();
        if (mCly.remoteConfigAutomaticUpdateEnabled && mCly.anyConsentGiven()) {
            mCly.moduleRemoteConfig.updateRemoteConfigValues(null, null, mCly.connectionQueue_, false, null);
        }

        mCly.doStoredRequests();
    }

    /**
     * Changes current device id type to the one specified in parameter. Closes current session and
     * reopens new one with new id. Doesn't merge user profiles on the server
     *
     * @param type Device ID type to change to
     * @param deviceId Optional device ID for a case when type = DEVELOPER_SPECIFIED
     */
    void changeDeviceIdWithoutMerge(DeviceId.Type type, String deviceId) {
        if (type == null) {
            throw new IllegalStateException("type cannot be null");
        }

        if (type == DeviceId.Type.DEVELOPER_SUPPLIED && deviceId == null) {
            throw new IllegalStateException("WHen type is 'DEVELOPER_SUPPLIED', provided deviceId cannot be null");
        }

        if (!mCly.anyConsentGiven() && type != DeviceId.Type.TEMPORARY_ID) {
            // if we are not trying to set a temporary id, consent has to be given
            moduleLog.e("[ModuleDeviceId] Can't change Device ID if no consent is given");
            return;
        }

        DeviceId currentDeviceId = mCly.connectionQueue_.getDeviceId();

        if (currentDeviceId.temporaryIdModeEnabled()
                && (deviceId != null && deviceId.equals(DeviceId.temporaryCountlyDeviceId))) {
            // we already are in temporary mode and we want to set temporary mode
            // in this case we just ignore the request since nothing has to be done
            return;
        }

        if (currentDeviceId.temporaryIdModeEnabled() || mCly.connectionQueue_.queueContainsTemporaryIdItems()) {
            // we are about to exit temporary ID mode
            // because of the previous check, we know that the new type is a different one
            // we just call our method for exiting it
            // we don't end the session, we just update the device ID and connection queue
            exitTemporaryIdMode(type, deviceId);
        }

        // we are either making a simple ID change or entering temporary mode
        // in both cases we act the same as the temporary ID requests will be updated with the final ID later

        // force flush events so that they are associated correctly
        mCly.sendEventsForced();

        // update remote config_ values after id change if automatic update is enabled
        mCly.moduleRemoteConfig.clearAndDownloadAfterIdChange();

        mCly.moduleSessions.endSessionInternal(currentDeviceId.getId());
        currentDeviceId.changeToId(mCly.context_, mCly.connectionQueue_.getCountlyStore(), type, deviceId);
        mCly.moduleSessions.beginSessionInternal();

        // clear automated star rating session values because now we have a new user
        mCly.moduleRatings.clearAutomaticStarRatingSessionCountInternal(mCly.connectionQueue_.getCountlyStore());
    }

    /**
     * Changes current device id to the one specified in parameter. Merges user profile with new id
     * (if any) with old profile.
     *
     * @param deviceId new device id
     */
    void changeDeviceIdWithMerge(String deviceId) {
        if (deviceId == null || "".equals(deviceId)) {
            throw new IllegalStateException("deviceId cannot be null or empty");
        }

        if (!mCly.anyConsentGiven()) {
            moduleLog.e("[ModuleDeviceId] Can't change Device ID if no consent is given");
            return;
        }

        if (mCly.connectionQueue_.getDeviceId().temporaryIdModeEnabled()
                || mCly.connectionQueue_.queueContainsTemporaryIdItems()) {
            // if we are in temporary ID mode or
            // at some moment have enabled temporary mode

            if (deviceId.equals(DeviceId.temporaryCountlyDeviceId)) {
                // if we want to enter temporary ID mode
                // just exit, nothing to do

                moduleLog.w("[ModuleDeviceId, changeDeviceId] About to enter temporary ID mode when already in it");

                return;
            }

            // if a developer supplied ID is provided
            // we just exit this mode and set the id to the provided one
            exitTemporaryIdMode(DeviceId.Type.DEVELOPER_SUPPLIED, deviceId);
        } else {
            // we are not in temporary mode, nothing special happens
            // we are either making a simple ID change or entering temporary mode
            // in both cases we act the same as the temporary ID requests will be updated with the final ID later

            // update remote config_ values after id change if automatic update is enabled
            mCly.moduleRemoteConfig.clearAndDownloadAfterIdChange();

            mCly.connectionQueue_.changeDeviceId(deviceId,
                    mCly.moduleSessions.roundedSecondsSinceLastSessionDurationUpdate());
        }
    }

    @Override
    public void initFinished(CountlyConfig config) {
        if (isExitTempIdAfterInit) {
            moduleLog.i("[ModuleDeviceId, initFinished] Exiting temp ID at the end of init");
            exitTemporaryIdMode(DeviceId.Type.DEVELOPER_SUPPLIED, config.deviceID);
        }
    }

    @Override
    void halt() {
    }

}
